<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <script>
      const obj = { name: "孙悟空", age: 18 };
      const obj1 = { name: "猪八戒", age: 25 };
      /**
       * 工作的一套思维模板
       * 1.理解需求：我要实现什么目标？(手写一个call和apply方法)
       * 2.分析需求：
       *    - call和apply都是用于改变函数的this指向的，并不会返回新函数
       *    - 俩者只有传参方式的不同，apply传数组，call直接传
       *
       */
      Function.prototype.myCall = function (context) {
        // 非函数调用，直接抛出错误
        if (typeof this !== "function") {
          throw new Error("myBind must be called on a function");
        }
        // 设置上下文
        // 如果没有传入上下文，默认为全局对象（浏览器中的 window 或 Node.js 中的 globalThis）
        context =
          context || (typeof window !== "undefined" ? window : globalThis);

        // 创建一个唯一的符号作为键名
        // 使用 Symbol 确保不会与上下文对象的其他属性冲突
        const fnKey = Symbol("fn");

        // 将目标函数绑定到上下文对象上
        // 这样可以确保在调用时，目标函数的 this 指向上下文对象
        context[fnKey] = this;

        // 获取从第二个参数开始的所有参数
        const args = [...arguments].slice(1);
        // 将参数传递过去，并且执行
        const result = context[fnKey](...args);

        // 删除临时方法
        // 避免污染上下文对象
        delete context[fnKey];

        // 返回目标函数的执行结果
        return result;
      };

      Function.prototype.myapply = function (context, args) {
        // 非函数调用，直接抛出错误
        if (typeof this !== "function") {
          throw new Error("myapply must be called on a function");
        }

        // 设置上下文
        // 如果没有传入上下文，默认为全局对象（浏览器中的 window 或 Node.js 中的 globalThis）
        context =
          context || (typeof window !== "undefined" ? window : globalThis);

        // 创建一个唯一的符号作为键名
        // 使用 Symbol 确保不会与上下文对象的其他属性冲突
        const fnKey = Symbol("fn");

        // 将目标函数绑定到上下文对象上
        // 这样可以确保在调用时，目标函数的 this 指向上下文对象
        context[fnKey] = this;

        // 检查 args 是否为数组
        if (args === undefined) {
          args = []; // 如果没有传 args，默认为一个空数组
        } else if (!Array.isArray(args)) {
          throw new TypeError("Second argument to myapply must be an array");
        }

        // 将参数传递过去，并且执行
        const result = context[fnKey](...args);

        // 删除临时方法
        // 避免污染上下文对象
        delete context[fnKey];

        // 返回目标函数的执行结果
        return result;
      };

      function fn(...args) {
        console.log("fn中的this", this);
        for (let item of args) {
          console.log(item);
        }
      }

      fn.myCall(obj, 1, 2, 3);
      fn.myapply(obj1, [1,2,3]); // 1,2,3
      fn.myapply(obj1);// 不传，正常打印this
      fn.myapply(obj1, 1, 2, 3); // 传参格式正确！ 报错

    </script>
  </body>
</html>
